package com.bes.aiProject.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bes.aiProject.config.MyUtils;
import com.bes.aiProject.entry.SxAAbnormalResult;
import com.bes.aiProject.entry.SxAAnalyzeMetricInfo;
import com.bes.aiProject.entry.SxAJobRecord;
import com.bes.aiProject.mapper.SxAAbnormalResultMapper;
import com.bes.aiProject.mapper.SxAAnalyzeMetricInfoMapper;
import com.bes.aiProject.mapper.SxAJobRecordMapper;
import com.bes.aiProject.service.AIService;
import com.bes.aiProject.service.AnomalyCheckService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AnomalyCheckServiceImpl implements AnomalyCheckService {

    @Value("${task.group-condition}")
    private String groupCondition;

    @Autowired
    AIService aiService;
    @Autowired
    SxAJobRecordMapper recordMapper;
    @Autowired
    SxAAnalyzeMetricInfoMapper metricInfoMapper;
    @Autowired
    SxAAbnormalResultMapper resultMapper;
    Gson gson = new Gson();

    public void AnomalyCheck(LocalDateTime currentTime) {
        LocalDateTime lastRecordTime = getLastRecordTime(currentTime);
        //获取历史数据并聚合，用于计算上下阈值
        List<SxAAnalyzeMetricInfo> groupMetrics = metricInfoMapper.getGroupMetric(groupCondition, lastRecordTime);
        //获取待检测数据（不聚合）
        List<SxAAnalyzeMetricInfo> predictMetric = metricInfoMapper.getPredictMetric(groupCondition, lastRecordTime);
        Map<String, Map<String, List<SxAAnalyzeMetricInfo>>> groupMetricsMap =
                groupMetrics.stream().collect(Collectors.groupingBy(SxAAnalyzeMetricInfo::getConditionKey, Collectors.groupingBy(SxAAnalyzeMetricInfo::getMetricCode)));
        Map<String, Map<String, List<SxAAnalyzeMetricInfo>>> predictMetricMap =
                predictMetric.stream().collect(Collectors.groupingBy(SxAAnalyzeMetricInfo::getConditionKey, Collectors.groupingBy(SxAAnalyzeMetricInfo::getMetricCode)));
        //循环按照分组+指标进行检测
        //先循环分组
        for (String groupKey : groupMetricsMap.keySet()) {
            //循环指标
            for (String metricKey : groupMetricsMap.get(groupKey).keySet()) {
                List<SxAAnalyzeMetricInfo> metricDataList = groupMetricsMap.get(groupKey).get(metricKey);
                //历史数据不足，插入执行记录结束
                if (metricDataList.size() < 100) {
                    saveAnomalyJobRecord(metricDataList.get(0), currentTime, MyUtils.RESULT_STATUS.ERROR, MyUtils.MSG.NO_HISTORY);
                    continue;
                }
                //历史数据充足，检查有无待检查数据
                List<SxAAnalyzeMetricInfo> forPredict = predictMetricMap.get(groupKey).get(metricKey);
                //上次任务到这次任务之间没有待检测数据，返回执行记录成功
                if (CollectionUtils.isEmpty(forPredict)) {
                    saveAnomalyJobRecord(metricDataList.get(0), currentTime, MyUtils.RESULT_STATUS.SUCCESS, MyUtils.MSG.NO_PREDICT);
                    continue;
                }
                //组装参数调用AI
                Map<String, Object> requestMap = new HashMap<>();
                List<Map<String, Object>> historyDataArray = new ArrayList<>();
                requestMap.put("historyData", historyDataArray);
                List<LocalDateTime> historyCheckTime = metricDataList.stream().map(SxAAnalyzeMetricInfo::getMetricTime).collect(Collectors.toList());
                List<Double> historyCheckValue = metricDataList.stream().map(SxAAnalyzeMetricInfo::getValue).collect(Collectors.toList());
                HashMap<String, Object> historyData = new HashMap<>();
                historyData.put("metric", metricKey);
                historyData.put("time", historyCheckTime);
                historyData.put("value", historyCheckValue);
                historyDataArray.add(historyData);
                HashMap resultMap = aiService.callAnomalyAI(requestMap);
                if (CollectionUtils.isEmpty(resultMap)) {
                    saveAnomalyJobRecord(metricDataList.get(0), currentTime, MyUtils.RESULT_STATUS.ERROR, MyUtils.MSG.AI_CALL_ERROR);
                    log.error("异常检测调用AI接口报错,任务终止！");
                    continue;
                }
                HashMap data = (HashMap) resultMap.get("data");
                if (0 == (int) resultMap.get("code")) {
                    saveAnomalyJobRecord(metricDataList.get(0), currentTime, MyUtils.RESULT_STATUS.SUCCESS, MyUtils.MSG.SUCCESS);
                    log.info("异常检测调用AI接口成功！ message:<{}>", resultMap.get("msg"));
                    List<Map<String, Object>> thresholds = (List<Map<String, Object>>) data.get("result");
                    saveAnomalyResult(forPredict, thresholds, currentTime);
                    log.info("异常检测结果保存成功!");
                } else {
                    saveAnomalyJobRecord(metricDataList.get(0), currentTime, MyUtils.RESULT_STATUS.ERROR, MyUtils.MSG.AI_REQUEST_ERROR + resultMap.get("msg").toString().substring(900));
                    log.info("异常检测调用AI接口失败！ message:<{}>", resultMap.get("msg"));
                }
            }


        }
    }

    private LocalDateTime getLastRecordTime(LocalDateTime currentTime) {
        SxAJobRecord lastRecord = recordMapper.selectOne(new QueryWrapper<SxAJobRecord>().select("MAX(create_date) as create_date"));
        return lastRecord == null ? currentTime.minusDays(1) : lastRecord.getCreateDate();
    }

    private void saveAnomalyResult(List<SxAAnalyzeMetricInfo> forPredict, List<Map<String, Object>> thresholds, LocalDateTime currentTime) {
        SxAAbnormalResult abnormalResult = new SxAAbnormalResult();
        abnormalResult.setId(MyUtils.uuidForInt());
        abnormalResult.setJobId(MyUtils.JOB.ANOMALY_CHECK.getJobId());
        abnormalResult.setJobName(MyUtils.JOB.ANOMALY_CHECK.getJobName());
        abnormalResult.setCreateDate(currentTime);
        double upThreshold = (Double) thresholds.get(0).get("UP");
        double downThreshold = (Double) thresholds.get(0).get("DOWN");
        for (SxAAnalyzeMetricInfo predict : forPredict) {
            abnormalResult.setMetricCode(predict.getMetricCode());
            abnormalResult.setMetricName(predict.getMetricName());
            abnormalResult.setEnterpriseCode(predict.getEnterpriseCode());
            abnormalResult.setEnterpriseName(predict.getEnterpriseName());
            abnormalResult.setBusinessCode(predict.getBusinessCode());
            abnormalResult.setBusinessName(predict.getBusinessName());
            abnormalResult.setTaskCode(predict.getTaskCode());
            abnormalResult.setTaskName(predict.getTaskName());
            abnormalResult.setStepCode(predict.getStepCode());
            abnormalResult.setStepName(predict.getStepName());
            abnormalResult.setAreaCode(predict.getAreaCode());
            abnormalResult.setAreaName(predict.getAreaName());
            abnormalResult.setCustomerType(predict.getCustomerType());
            abnormalResult.setCustomerName(predict.getCustomerName());
            abnormalResult.setAbnormalTime(predict.getMetricTime());
            abnormalResult.setValue(predict.getValue());
            abnormalResult.setErrorInfo(gson.toJson(thresholds));
            //低于下阈值
            if (Double.compare(predict.getValue(), downThreshold) < 0) {
                abnormalResult.setAbnormalType(MyUtils.ANOMALY_RESULT.DOWN.name());
            } else if (Double.compare(predict.getValue(), upThreshold) > 0) {
                abnormalResult.setAbnormalType(MyUtils.ANOMALY_RESULT.UP.name());
            } else {
                abnormalResult.setAbnormalType(MyUtils.ANOMALY_RESULT.NORMAL.name());
            }
            resultMapper.insert(abnormalResult);
        }
    }

    private void saveAnomalyJobRecord(SxAAnalyzeMetricInfo sxAAnalyzeMetricInfo, LocalDateTime currentTime, MyUtils.RESULT_STATUS resultStatus, String message) {
        SxAJobRecord jobRecord = new SxAJobRecord();
        jobRecord.setId(MyUtils.uuidForInt());
        jobRecord.setJobId(MyUtils.JOB.ANOMALY_CHECK.getJobId());
        jobRecord.setJobName(MyUtils.JOB.ANOMALY_CHECK.getJobName());
        jobRecord.setMetricCode(sxAAnalyzeMetricInfo.getMetricCode());
        jobRecord.setMetricName(sxAAnalyzeMetricInfo.getMetricName());
        jobRecord.setCreateDate(currentTime);
        jobRecord.setEnterpriseCode(sxAAnalyzeMetricInfo.getEnterpriseCode());
        jobRecord.setEnterpriseName(sxAAnalyzeMetricInfo.getEnterpriseName());
        jobRecord.setBusinessCode(sxAAnalyzeMetricInfo.getBusinessCode());
        jobRecord.setBusinessName(sxAAnalyzeMetricInfo.getBusinessName());
        jobRecord.setTaskCode(sxAAnalyzeMetricInfo.getTaskCode());
        jobRecord.setTaskName(sxAAnalyzeMetricInfo.getTaskName());
        jobRecord.setStepCode(sxAAnalyzeMetricInfo.getStepCode());
        jobRecord.setAreaCode(sxAAnalyzeMetricInfo.getAreaCode());
        jobRecord.setAreaName(sxAAnalyzeMetricInfo.getAreaName());
        jobRecord.setCustomerType(sxAAnalyzeMetricInfo.getCustomerType());
        jobRecord.setCustomerName(sxAAnalyzeMetricInfo.getCustomerName());
        jobRecord.setResultStatus(resultStatus.name());
        jobRecord.setErrorInfo(message);
        recordMapper.insert(jobRecord);
    }


}

